home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1996 / MacHack 1996.toast / Hacks / Hacks ’89 / gadlife / source (ugly) / patch.sound.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-06-14  |  7.8 KB  |  314 lines  |  [TEXT/KAHL]

  1. #include "main.h"
  2. #include "patch.h"
  3. #include "patch.sound.h"
  4.  
  5. /*******************************************************
  6. *                                                                *
  7. *                                                                *
  8. *******************************************************/
  9.  
  10. extern int            isBackground;
  11.  
  12. static int            soundTrapOn = 0, chPlaying = 0;
  13. static long            oldSndPlay;
  14. static chPtr        theChannels = NULL, chTail = NULL;
  15. static sndHdl        theSounds = NULL;
  16.  
  17. /*******************************************************
  18. *                                                                *
  19. *    killChannels is called when the application quits to kill off any sound that may    *
  20. *    still be playing and placate the sound manager.                            *
  21. *                                                                *
  22. *******************************************************/
  23.  
  24. disposeCh( quitNow )
  25.     int        quitNow;
  26.     {
  27.     chPtr    next;
  28.     sndHdl    theSnd = theChannels->theSnd;
  29.     
  30.     if( theChannels->chan )        SndDisposeChannel( theChannels->chan, quitNow );
  31.     if( theSnd && *theSnd )        disposeSnd( theSnd );
  32.     if( chTail == theChannels )        chTail = NULL;
  33.     next = theChannels->next;
  34.     DisposPtr( theChannels );
  35.     theChannels = next;
  36.     --isBackground;
  37.     }
  38.  
  39. disposeSnd( theSnd )
  40.     sndHdl    theSnd;
  41.     {
  42.     Handle    sndData;
  43.     sndPtr    thePtr;
  44.     
  45.     HLock( theSnd );
  46.     thePtr = *theSnd;
  47.     if( --( thePtr->useCount ) == 0 )
  48.         {
  49.         sndData = thePtr->theData;
  50.         if( sndData && *sndData )
  51.             DisposHandle( sndData );
  52.         if( thePtr->next ) ( **( thePtr->next )).prev = thePtr->prev;
  53.         if( thePtr->prev ) ( **( thePtr->prev )).next = thePtr->next;
  54.         if( theSounds == theSnd ) theSounds = thePtr->next;
  55.         DisposHandle( theSnd );
  56.         }
  57.     else    HUnlock( theSnd );
  58.     }
  59.  
  60. killChannels()
  61.     {
  62.     while( theChannels ) disposeCh( -1 );        /* force to stop */
  63.     chPlaying = 0;
  64.     }
  65.  
  66. /*******************************************************
  67. *                                                                *
  68. *    This uses the globals chPlaying and theChannels.  DisposeCh diposes the first    *
  69. *    channel in the queue.  StartUpCh returns an error if it can't start it up.        *
  70. *                                                                *
  71. *******************************************************/
  72.  
  73. finishChannels()
  74.     {
  75.     if( theChannels && theChannels->stage == chStopped )
  76.         chPlaying = 0;
  77.     while( theChannels && theChannels->stage == chStopped )
  78.         disposeCh( 0 );
  79.     if( !chPlaying )
  80.         while( theChannels && startUpCh( theChannels )) 
  81.             disposeCh( 0 );
  82.     else if( theChannels->stage == chStarted )
  83.         sendCallBack( theChannels );
  84.     }
  85.  
  86. /*******************************************************
  87. *                                                                *
  88. *    doCallBack simply sets a flag in the current channel record saying that it is    *
  89. *    completed.  The address of this is passed as param2, so that globals are not    *
  90. *    really needed.  Also, as a0 is the only register used, and this is allowed by    *
  91. *    both LSC and the Sound Manager to be munged, I go right ahead.                *
  92. *                                                                *
  93. *******************************************************/
  94.  
  95. pascal doCallBack( chan, cmd )
  96.     SndCommand    *cmd;
  97.     SndChannelPtr    chan;
  98.     {
  99.     asm    {
  100.         move.l    cmd,a0
  101.         move.l    4(a0),a0
  102.         move.w    #chStopped,(a0)
  103.         }
  104.     }
  105.  
  106. /*******************************************************
  107. *                                                                *
  108. *                                                                *
  109. *******************************************************/
  110.  
  111. int startUpCh( theCh )
  112.     chPtr    theCh;
  113.     {
  114.     sndHdl    theSnd = theCh->theSnd;
  115.     Handle    theData;
  116.     
  117.     if( theSnd && *theSnd )
  118.         {
  119.         theData = ( **theSnd ).theData;
  120.         if( theData && *theData &&
  121.             !SndNewChannel( &( theCh->chan ), 0, NULL, doCallBack ) &&
  122.             !SndPlay( theCh->chan, theData, -1 ))
  123.                 {
  124.                 chPlaying = -1;
  125.                 theCh->stage = chStarted;
  126.                 sendCallBack( theCh );
  127.                 return( noErr );
  128.                 }
  129.         }
  130.     theCh->stage = chStopped;
  131.     return( -1 );
  132.     }
  133.  
  134. /*******************************************************
  135. *                                                                *
  136. *                                                                *
  137. *******************************************************/
  138.  
  139. sendCallBack( theCh )
  140.     chPtr        theCh;
  141.     {
  142.     SndCommand    theCmd;
  143.     int            result;
  144.     
  145.     theCmd.cmd = callBackCmd;
  146.     theCmd.param1 = 0;
  147.     theCmd.param2 = (long)theCh;
  148.     if( SndDoCommand( theCh->chan, &theCmd, -1 ))
  149.         ++theCh->tryCount;
  150.     else    theCh->stage = chSentCall;
  151.     }
  152.  
  153. /*******************************************************
  154. *                                                                *
  155. *                                                                *
  156. *******************************************************/
  157.  
  158. insertSnd( theSnd )
  159.     sndHdl    theSnd;
  160.     {
  161.     ( **theSnd ).next = theSounds;
  162.     ( **theSnd ).prev = NULL;
  163.     if( theSounds ) ( **theSounds ).prev = theSnd;
  164.     theSounds = theSnd;
  165.     }
  166.  
  167. sndHdl newSndCopy( theData )
  168.     Handle    theData;
  169.     {
  170.     sndHdl    theSnd = NULL;
  171.     sndPtr    thePtr;
  172.     long        type = 0;
  173.     int        id = 0, homeFile = -1, found = 0;
  174.     char        deadspace[256];
  175.     
  176.     if(( homeFile = HomeResFile( theData )) != -1 )
  177.         {
  178.         GetResInfo( theData, &id, &type, deadspace );
  179.         theSnd = theSounds;
  180.         while(     theSnd && 
  181.                 ( thePtr = *theSnd ) && (
  182.                 thePtr->rsrcFile != homeFile ||
  183.                 thePtr->rsrcType != type ||
  184.                 thePtr->rsrcID != id ))
  185.             theSnd = thePtr->next;
  186.         }
  187.     if( theSnd ) ++( **theSnd ).useCount;
  188.     else    {
  189.         HandToHand( &theData );
  190.         if( MemError() ) return( 0L );
  191.         theSnd = ( sndHdl )NewHandle(( long )sizeof( sndInfo ));
  192.         if( MemError() ) return( 0L );
  193.         thePtr = *theSnd;
  194.         thePtr->rsrcFile = homeFile;
  195.         thePtr->rsrcType = type;
  196.         thePtr->rsrcID = id;
  197.         thePtr->useCount = 1;
  198.         thePtr->theData = theData;
  199.         insertSnd( theSnd );
  200.         }
  201.     return( theSnd );
  202.     }
  203.  
  204. /*******************************************************
  205. *                                                                *
  206. *                                                                *
  207. *******************************************************/
  208.  
  209. insertCh( newCh )
  210.     chPtr    newCh;
  211.     {
  212.     if( chTail ) 
  213.         {
  214.         chTail->next = newCh;
  215.         chTail = newCh;
  216.         }
  217.     else chTail = theChannels = newCh;
  218.     newCh->next = NULL;
  219.     }
  220.  
  221. chPtr newChannel( theSnd )
  222.     Handle    theSnd;
  223.     {
  224.     chPtr    newCh;
  225.     sndHdl    sndCopy;
  226.     
  227.     if( theSnd && ( sndCopy = newSndCopy( theSnd )))
  228.         {
  229.         newCh = (chPtr)NewPtr(( long )sizeof( chInfo ));
  230.         if( !MemError() )
  231.             {
  232.             ++isBackground;
  233.             insertCh( newCh );
  234.             newCh->chan = NULL;        /* open a channel later when we start to play the sound */
  235.             newCh->theSnd = sndCopy;
  236.             newCh->stage = chCreated;
  237.             newCh->tryCount = 0;
  238.             return( newCh );
  239.             }
  240.         }
  241.     return( 0L );
  242.     }
  243.  
  244. /*******************************************************
  245. *                                                                *
  246. *    I use a trap during SysBeep instead of just replacing it, as it does a bunch of    *
  247. *    error checking, takes care of blinking the menubar, and finds and loads the        *
  248. *    sound resource that has been chosen with the Control Panel.  Rather than, as    *
  249. *    with file IO, executing the instruction asyncronously while doing my stuff, I    *
  250. *    return immediately, so that response time picks up, and things feel faster.    *
  251. *    In order for this to work properly, I have to make sure that the caller does not    *
  252. *    de-allocate the sound while it is being played, so I make a copy of the resource.    *
  253. *    If the same sound is used more than once, I only copy it once, and keep a use    *
  254. *    count.  If there is a channel provided, I pass it through.  What could be done    *
  255. *    instead is chew my cud until all old calls finish, and then: If async, play it as    *
  256. *    requested, but follow it with a special callback, so I know when it's done.  If    *
  257. *    sync, make it async and play it while waiting around for it to finish.  Similarly,    *
  258. *    I could patch the other sound calls to make sure nothing ever got through.  This    *
  259. *    seems silly though….                                                *
  260. *                                                                *
  261. *******************************************************/
  262.  
  263. pascal int doSndPlay( chan, sndHdl, async )
  264.     SndChannelPtr    chan;
  265.     Handle        sndHdl;
  266.     int            async;
  267. {
  268.     int            err;
  269.     asm {
  270.             movem.l    d0-d7/a1-a5,-(sp)
  271.             move.l    CurrentA5,a5
  272.     }
  273.     if( chan ) asm {
  274.             move.l    oldSndPlay,a0
  275.             movem.l    (sp)+,d0-d7/a1-a5
  276.             unlk        a6
  277.             jmp        (a0)
  278.     }
  279.     err = newChannel( sndHdl ) ? noErr : resProblem;
  280.     asm {
  281.         movem.l    (sp)+,d0-d7/a1-a5
  282.     }
  283.     return( err );
  284. }
  285.  
  286. installSndPatch()
  287. {
  288.     if( !soundTrapOn ) {
  289.         oldSndPlay = NGetTrapAddress( SndPlayTrapNum, ToolTrap );
  290.         NSetTrapAddress( doSndPlay, SndPlayTrapNum, ToolTrap );
  291.     }
  292.     ++soundTrapOn;
  293. }
  294.  
  295. removeSndPatch()
  296. {
  297.     if( soundTrapOn == 1 )
  298.         NSetTrapAddress( oldSndPlay, SndPlayTrapNum, ToolTrap );
  299.     if( soundTrapOn > 0 ) --soundTrapOn;
  300. }
  301.  
  302. pascal doErrSound( which )
  303.     int        which;
  304. {
  305.     if( which ) doSysBeep( 5 );
  306. }
  307.  
  308. doSysBeep( count )
  309.     int    count;
  310. {
  311.     installSndPatch();
  312.     SysBeep( count );
  313.     removeSndPatch();
  314. }